#pragma once

#include "../sqlite/sqlite.hpp"
#include "property_database_literals.hpp"
#include <sstream>
#include <map>

namespace umtl
{
	//////////////////////////////////////////////////////////////////////////
	//

	template< typename StringType >
	class property_database
	{
	public:
		typedef sqlite<StringType>							Database;
		typedef StringType									StringType;
		typedef typename StringType::value_type				CharType;
		typedef typename Database::Params					Params;
		typedef typename Database::Results					Results;
		typedef std::basic_stringstream<CharType>			QueryStream;
		typedef std::map<StringType, StringType>			Cache;

		property_database( StringType const & filename );
		virtual ~property_database();

		void set( StringType const & name, StringType const & value );
		void remove( StringType const & name );
		StringType get( StringType const & name );

	private:
		bool exists_table();
		void update( StringType const & name, StringType const & value );

		Database db_;
		StringType filename_;
		Cache cache;

		property_database( property_database const & );
		void operator=( property_database const & );
	};

	//////////////////////////////////////////////////////////////////////////
	//
}

namespace umtl
{

	//////////////////////////////////////////////////////////////////////////
	//

	template< typename StringType >
	property_database<StringType>::property_database( StringType const & filename )
		: db_( filename )
		, filename_( filename )
	{
		if ( exists_table() == false )
		{
			QueryStream query_stream;

			query_stream << property_database_literal::create_table< CharType >::literal();

			db_.execute( query_stream.str() );
		}
	}

	//////////////////////////////////////////////////////////////////////////
	//

	template< typename StringType >
	property_database<StringType>::~property_database() {}

	//////////////////////////////////////////////////////////////////////////
	//

	template< typename StringType >
	void property_database<StringType>::set( StringType const & name, StringType const & value )
	{
		if ( get(name).empty() ) // is not exists
		{
			QueryStream query_stream;

			query_stream << property_database_literal::set1< CharType >::literal()
				<< name << property_database_literal::set2< CharType >::literal()
				<< value << property_database_literal::set3< CharType >::literal();

			db_.execute( query_stream.str() );
		}
		else
		{
			update( name, value );
		}

		cache[name] = value;
	}

	//////////////////////////////////////////////////////////////////////////
	//

	template< typename StringType >
	void property_database<StringType>::remove( StringType const & name )
	{
		QueryStream query_stream;

		query_stream 
			<< property_database_literal::remove1< CharType >::literal() << name 
			<< property_database_literal::remove2< CharType >::literal();

		db_.execute( query_stream.str() );

		cache.erase( name );
	}

	//////////////////////////////////////////////////////////////////////////
	//

	template< typename StringType >
	StringType property_database<StringType>::get( StringType const & name )
	{
		auto cache_iter = cache.find(name);

		if( cache_iter != cache.end() )
		{
			return cache_iter->second;
		}

		QueryStream query_stream;

		query_stream << property_database_literal::get1< CharType >::literal()
			<< name
			<< property_database_literal::get2< CharType >::literal();

		Results results;

		db_.execute( query_stream.str(), results );

		if( results.empty() == false )
		{
			cache[name] = results[0].second;
			return results[0].second;
		}

		return StringType();
	}

	//////////////////////////////////////////////////////////////////////////
	//

	template< typename StringType >
	bool property_database<StringType>::exists_table()
	{
		QueryStream query_stream;

		query_stream << property_database_literal::exists_table< CharType >::literal();

		Results results;

		db_.execute( query_stream.str(), results );

		return results.empty() == false;
	}

	//////////////////////////////////////////////////////////////////////////
	//

	template< typename StringType >
	void property_database<StringType>::update( StringType const & name, StringType const & value )
	{
		QueryStream query_stream;

		query_stream << property_database_literal::update1< CharType >::literal() 
			<< value << property_database_literal::update2< CharType >::literal() 
			<< name << property_database_literal::update3< CharType >::literal();

		db_.execute( query_stream.str() );
	}

	//////////////////////////////////////////////////////////////////////////
}